//
// FileCopier
//
// Copyright (C) jextra.net.
//
//  This file is part of the Crack build system.
//
//  The Crack build system is free software; you can redistribute it 
//  and/or modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  The Crack build system is distributed in the hope that it will be 
//  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with the Crack build system; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
//  02111-1307 USA.
//

package net.jextra.crack.tool;

import java.io.*;
import java.net.*;
import net.jextra.crack.type.*;

/**
 * <p>
 * Copies single files or entire directories.
 * </p>
 */
public class FileCopier
{
    // ============================================================
    // Enums
    // ============================================================

    public enum Type
    {
        nonExisting,
        older,
        always;
    }

    // ============================================================
    // Fields
    // ============================================================

    private int BUFFER_SIZE = 10000;

    // ============================================================
    // Methods
    // ============================================================

    // ----------
    // public
    // ----------

    public boolean copyDir( File from, File to )
        throws IOException
    {
        return copyDir( from, to, Type.always );
    }

    public boolean copyDir( File from, File to, Type copyType )
        throws IOException
    {
        return copyDir( from, null, to, copyType );
    }

    public boolean copyDir( File from, FileStrainer strainer, File to, Type copyType )
        throws IOException
    {
        assert from != null : "From directory can not be null.";
        assert from.exists() : "From directory does not exist.";
        assert from.isDirectory() : "From directory must be a directory.";
        assert to != null : "To directory can not be null.";
        assert to.isDirectory() : "To directory " + to.getCanonicalPath() + " must be a directory.";

        System.out.printf( "Copying directory [%s] to [%s].\n", from.getCanonicalPath(),
            to.getCanonicalPath() );

        if ( strainer == null )
        {
            strainer = new RegexFileStrainer();
        }

        boolean success = true;
        for ( File file : strainer.getFiles( from ) )
        {
            String path = FileTool.getRelativePath( from, file.getParentFile() );

            File toDir = new File( to, path );
            if ( !toDir.exists() )
            {
                toDir.mkdirs();
            }

            copyFile( file, new File( toDir, file.getName() ), copyType );
        }

        return success;
    }

    public boolean copyFileSet( FileSet from, File to )
        throws IOException
    {
        return copyFileSet( from, to, Type.always );
    }

    public boolean copyFileSet( FileSet from, File to, Type copyType )
        throws IOException
    {
        for ( File file : from )
        {
            boolean success = copyFile( file, to, copyType );
            if ( !success )
            {
                return false;
            }
        }

        return true;
    }

    public boolean copyFile( File from, File to )
        throws IOException
    {
        return copyFile( from, to, Type.always );
    }

    public boolean copyFile( File from, File to, Type copyType )
        throws IOException
    {
        assert from.exists() : String.format( "From file [%s] does not exist.",
            from.getCanonicalPath() );
        assert from.isFile() : String.format( "From file [%s] must be a file.",
            from.getCanonicalPath() );

        if ( to.isDirectory() )
        {
            to = new File( to, from.getName() );
        }

        if ( to.exists() )
        {
            switch ( copyType )
            {
                case nonExisting:
                    return false;

                case older:
                    if ( from.lastModified() <= to.lastModified() )
                    {
                        return false;
                    }
                    break;
            }
        }

        System.out.printf( "Copying file [%s] to [%s].\n", from.getCanonicalPath(),
            to.getCanonicalPath() );

        FileInputStream in = new FileInputStream( from );
        copyStreamInternal( in, to );

        return true;
    }

    public boolean copyStream( InputStream from, File to, long timestamp )
        throws IOException
    {
        System.out.printf( "Copying file to [%s].\n", to.getCanonicalPath() );

        boolean success = copyStream( from, to );
        if ( !success )
        {
            return false;
        }

        return true;
    }

    public boolean copyStream( InputStream from, File to )
        throws IOException
    {
        assert from != null : "From input stream can not be null.";
        assert to != null : "To file can not be null.";
        assert !to.isDirectory() : "To file can not be directory.";

        System.out.printf( "Copying file to [%s].\n", to.getCanonicalPath() );

        to.getParentFile().mkdirs();
        copyStreamInternal( from, to );

        return true;
    }

    public boolean copyStream( URL url, File to )
        throws IOException
    {
        assert url != null : "url input stream can not be null.";
        assert to != null : "to file can not be null.";
        assert !to.isDirectory() : "to file can not be directory.";

        System.out.printf( "Copying file to [%s].\n", to.getCanonicalPath() );

        to.getParentFile().mkdirs();
        InputStream from = url.openStream();
        copyStreamInternal( from, to );

        return true;
    }

    // ----------
    // private
    // ----------

    private void copyStreamInternal( InputStream from, File to )
        throws IOException
    {
        FileOutputStream out = new FileOutputStream( to );
        byte[] buf = new byte[BUFFER_SIZE];
        int i = 0;
        while ( ( i = from.read( buf ) ) != -1 )
        {
            out.write( buf, 0, i );
        }
        from.close();
        out.close();
    }
}

